package org.yu.team2023.flashdays.client;

import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Axis;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.Direction;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Pose;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.player.PlayerModelPart;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.yu.team2023.flashdays.client.render.SimpleRenderType;
import software.bernie.geckolib.util.RenderUtils;

public class FDRenderHelper {
    /**
     * 在指定坐标渲染有材质的项目
     * @param matrixStack PoseStack
     * @param partialTick partialTick
     * @param pos 坐标
     * @param renderType 渲染类型，其中通过设置ResourceLocation指定了材质
     * @param onLevelRenderer 具体渲染内容
     * @see SimpleRenderType
     * @see RenderType
     */
    public  static void renderOnPosition(PoseStack matrixStack, float partialTick, Vector3f pos, RenderType renderType, onLevelRenderer onLevelRenderer){
        var bufferSource= Minecraft.getInstance().renderBuffers().bufferSource();
        float px = pos.x();
        float py = pos.y();
        float pz = pos.z();
        VertexConsumer builder = bufferSource.getBuffer(renderType);
        matrixStack.pushPose();
        bindMainCameraPos(matrixStack,px,py,pz);
        Matrix4f matrix = matrixStack.last().pose();
        //render
        onLevelRenderer.onRenderer(matrixStack,builder,matrix,partialTick);

        matrixStack.popPose();

        RenderSystem.disableDepthTest();
        bufferSource.endBatch(renderType);
    }

    /**
     * 绑定相机相对位置
     * @param stack PoseStack
     * @see FDRenderHelper
     */
    public static void bindMainCameraPos(PoseStack stack, double x, double y, double z){
        var camera=Minecraft.getInstance().gameRenderer.getMainCamera();
        Vec3 projectedView = camera.getPosition();
        stack.translate(x-projectedView.x,y -projectedView.y,z -projectedView.z);
    }
    /**
     * 绑定相机相对位置
     * @param stack PoseStack
     * @see FDRenderHelper
     */
    public static void bindEntityCameraPos(PoseStack stack, double x, double y, double z){
        var camera=Minecraft.getInstance().getBlockEntityRenderDispatcher().camera;
        Vec3 projectedView = camera.getPosition();
        stack.translate(x-projectedView.x,y -projectedView.y,z -projectedView.z);
    }
    public static void bindEntityPos(PoseStack stack, Entity entity,float p){
        var pos=entity.getPosition(p);
        stack.translate(pos.x,pos.y,pos.z);
    }
    /**
     * 将模型旋转和实体旋转同步，考虑了倒立，骑乘的情况
     */
    public static void rotateModelByEntity(PoseStack poseStack, LivingEntity livingEntity, float partialTick) {
        boolean shouldSit = livingEntity.isPassenger() && (livingEntity.getVehicle() != null && livingEntity.getVehicle().shouldRiderSit());
        float lerpBodyRot = Mth.rotLerp(partialTick, livingEntity.yBodyRotO, livingEntity.yBodyRot);
        float lerpHeadRot = Mth.rotLerp(partialTick, livingEntity.yHeadRotO, livingEntity.yHeadRot);
        float netHeadYaw;

        if (shouldSit && livingEntity.getVehicle() instanceof LivingEntity livingentity) {
            lerpBodyRot = Mth.rotLerp(partialTick, livingentity.yBodyRotO, livingentity.yBodyRot);
            netHeadYaw = lerpHeadRot - lerpBodyRot;
            float clampedHeadYaw = Mth.clamp(Mth.wrapDegrees(netHeadYaw), -85, 85);
            lerpBodyRot = lerpHeadRot - clampedHeadYaw;

            if (clampedHeadYaw * clampedHeadYaw > 2500f)
                lerpBodyRot += clampedHeadYaw * 0.2f;
        }

        if (livingEntity.getPose() == Pose.SLEEPING) {
            Direction bedDirection = livingEntity.getBedOrientation();

            if (bedDirection != null) {
                float eyePosOffset = livingEntity.getEyeHeight(Pose.STANDING) - 0.1F;

                poseStack.translate(-bedDirection.getStepX() * eyePosOffset, 0, -bedDirection.getStepZ() * eyePosOffset);
            }
        }

        boolean finished = false;
        Pose pose = livingEntity.getPose();

        if (pose != Pose.SLEEPING)
            poseStack.mulPose(Axis.YP.rotationDegrees(180f - lerpBodyRot));

        if (livingEntity.deathTime > 0) {
            float deathRotation = (livingEntity.deathTime + partialTick - 1f) / 20f * 1.6f;

            poseStack.mulPose(Axis.ZP.rotationDegrees(Math.min(Mth.sqrt(deathRotation), 1) * 90f));
        } else if (livingEntity.isAutoSpinAttack()) {
            poseStack.mulPose(Axis.XP.rotationDegrees(-90f - livingEntity.getXRot()));
            poseStack.mulPose(Axis.YP.rotationDegrees((livingEntity.tickCount + partialTick) * -75f));
        } else if (pose == Pose.SLEEPING) {
            Direction bedOrientation = livingEntity.getBedOrientation();

            poseStack.mulPose(Axis.YP.rotationDegrees(bedOrientation != null ? RenderUtils.getDirectionAngle(bedOrientation) : lerpBodyRot));
            poseStack.mulPose(Axis.ZP.rotationDegrees(90f));
            poseStack.mulPose(Axis.YP.rotationDegrees(270f));
        } else if (pose == Pose.SWIMMING) {
            float swimAmount = livingEntity.getSwimAmount(partialTick);
            float rot = livingEntity.isInWater() || livingEntity.isInFluidType((fluidType, height) -> livingEntity.canSwimInFluidType(fluidType)) ? -90.0F - livingEntity.getXRot() : -90.0F;
            float lerpRot = Mth.lerp(swimAmount, 0.0F, rot);
            poseStack.mulPose(Axis.XP.rotationDegrees(lerpRot));
            poseStack.translate(0.0F, -1.0F, 0.3F);
        } else if (livingEntity.hasCustomName() || livingEntity instanceof Player) {
            String name = livingEntity.getName().getString();

            if (livingEntity instanceof Player player) {
                if (!player.isModelPartShown(PlayerModelPart.CAPE)) {
                    finished = true;
                }
            } else {
                name = ChatFormatting.stripFormatting(name);
            }
            if (!finished) {
                if (name != null && (name.equals("Dinnerbone") || name.equalsIgnoreCase("Grumm"))) {
                    poseStack.translate(0, livingEntity.getBbHeight() + 0.1f, 0);
                    poseStack.mulPose(Axis.ZP.rotationDegrees(180f));
                }
            }

        }

    }
    public interface onLevelRenderer {
        void onRenderer(PoseStack poseStack, VertexConsumer buffer, Matrix4f matrix,float partialTick);
    }
}
